/*****************************************************************************
   Linux PPF patcher v0.1-rc1
  
   Copyright (C), Daniel Ekstr�m <dv01dem@cs.umu.se>, 2007 - 2008
   More information can be found at http://oakstream.mine.nu

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
******************************************************************************/

#include <getopt.h>
#include <stdio.h>

#include "libppf.h"

#define VERSION "v0.1-rc1"

/*
 * Returns a string representing the error code
 */
const char *errorCode(int error, ppf_t *ppf) {
   static char errorString[1024];
   switch (error) {
      case 0x01:
         sprintf(errorString, "File %s is NOT a PPF file!", ppf->ppfName);
      case 0x02:
         sprintf(errorString, "PPF version not supported or unknown");
      case 0x03:
         sprintf(errorString, "No such PPF file: %s", ppf->ppfName);
      case 0x04:
         sprintf(errorString, "Error opening PPF file: %s", ppf->ppfName);
      case 0x05:
         sprintf(errorString, "Error closing PPF file: %s", ppf->ppfName);
      case 0x06:
         sprintf(errorString, "Error reading from PPF file: %s", ppf->ppfName);
      case 0x07:
         sprintf(errorString, "PPF file hasn't been loaded");
      case 0x08:
         sprintf(errorString, "No undo data available");
      case 0x11:
         sprintf(errorString, "No such file: %s", ppf->isoName);
      case 0x12:
         sprintf(errorString, "Error opening file: %s", ppf->isoName);
      case 0x13:
         sprintf(errorString, "Error closing file: %s", ppf->isoName);
      case 0x14:
         sprintf(errorString, "Error reading from file: %s", ppf->isoName);
      case 0x15:
         sprintf(errorString, "Error writing to file: %s", ppf->isoName);
      default:
         sprintf(errorString, "Unknown error code!");
   }
   return errorString;
}

/*
 * Print usage
 */
void usage() {
   fprintf(stderr, "Linux PPF patcher " VERSION "\n\n"
      "Usage: lppf ppf-patch [action] [image]\n"
      "Actions:\n"
      "   -p image  patch the given image\n"
      "   -u image  undo the given image\n"
      "   -h        print this help\n"
      "   -v        print version number and exit\n\n"
      "The undo action only works on v3.0 patches "
      "with undo data available.\n"
      "If nothing except the patch is given, lppf will "
      "print patch info and exit.\n\n"
      "Copyright (C), 2007 - 2008 Daniel Ekstr�m "
      "<dv01dem@cs.umu.se>\n");
}

int main(int argc, char **argv) {
   ppf_t ppf;
   int error, opt;
   char *isoName;

   // First argument must be a patch file and dump it if no more options
   // are given
   if (argc == 1) {
      usage();
      return 0;
   }

   ppf_init(&ppf);
   
   // Parse argument with getopt
   while ((opt = getopt(argc, argv, "hp:u:v")) != -1) {
      switch (opt) {
         case 'h':
            usage();
            return 0;

         case 'p':
            // Load PPF first
            if ((error = ppf_loadPatch(&ppf, argv[1])) != 0) {
               fprintf(stderr, "%s\n", errorCode(error, &ppf));
               return 1;
            }

            // Apply PPF data to file
            isoName = argv[optind - 1];
            fprintf(stderr, "Applying PPF data from %s...\n", ppf.ppfName);
            if ((error = ppf_applyPatch(&ppf, isoName, false)) != 0) {
               fprintf(stderr, "%s\n", errorCode(error, &ppf));
            } else {
               fprintf(stderr, "%d bytes in %d chunks successfully written to %s!\n",
                       ppf.totalSize, ppf.chunkCount, ppf.isoName);
            }
            return 0;

         case 'u':
            // Load PPF first
            if ((error = ppf_loadPatch(&ppf, argv[1])) != 0) {
               fprintf(stderr, "%s\n", errorCode(error, &ppf));
               return 1;
            }

            // Apply PPF undo data to file
            isoName = argv[optind - 1];
            fprintf(stderr, "Applying PPF undo data from %s...\n", ppf.ppfName);
            if ((error = ppf_applyPatch(&ppf, isoName, true)) != 0) {
               fprintf(stderr, "%s\n", errorCode(error, &ppf));
            } else {
               fprintf(stderr, "%d bytes in %d chunks successfully written to %s!\n",
                       ppf.totalSize, ppf.chunkCount, ppf.isoName);
            }
            return 0;

         case 'v':
            fprintf(stderr, "lppf-" VERSION "\n");
            return 0;

         case ':':
            fprintf(stderr, "Error: option '%c' needs an argument, aborting...\n", optopt);
            return 0;

         case '?':
            fprintf(stderr, "Error: unknown option '%c', aborting...\n", optopt);
            return 0;
      }
   }

   // If we've gotten this far without returning, no arguments was given
   if ((error = ppf_loadPatch(&ppf, argv[1])) != 0) {
      fprintf(stderr, "%s\n", errorCode(error, &ppf));
      return 1;
   }
   ppf_dumpInfo(&ppf);
    
   return 0;
}
